package com.asen.commons.core.utils;

import com.asen.commons.annotation.FieldTypeHandler;
import com.asen.commons.core.base.IntegerEnum;
import com.asen.commons.core.base.ObjectEnum;
import com.asen.commons.core.base.StringEnum;
import com.asen.commons.core.base.model.BaseEnumEntry;
import com.asen.commons.core.base.domain.Core;
import com.asen.commons.core.base.domain.RootModel;
import com.asen.commons.core.jdbc.handler.TypeHandler;
import com.asen.commons.core.jdbc.handler.TypeHandlerHolder;
import com.asen.commons.core.meta.entity.EntityField;
import com.asen.commons.core.meta.entity.EntityObject;
import com.asen.commons.core.meta.entity.FieldObject;
import com.asen.commons.core.meta.listener.PropertyProcessListener;
import com.asen.commons.core.meta.loader.EntityObjectLoader;
import com.asen.commons.utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mapping.model.SimpleTypeHolder;

import java.lang.reflect.*;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 实体工具类
 *
 * @author Gary
 * @version 1.0
 * @date 2018-11-09
 */
public class EntityUtils {

    private static final Logger logger = LoggerFactory.getLogger(EntityUtils.class);

    private static Pattern camelPattern = Pattern.compile("_[a-z]");

    private static SimpleTypeHolder simpleTypeHolder;

    /**
     * 获取实体别名
     *
     * @param clazzName
     * @return
     * @throws Exception
     */
    public static String getEntityAlias(String clazzName) throws Exception {
        if (StringUtils.isEmpty(clazzName)) {
            return null;
        }

        return getEntityAlias(Class.forName(clazzName));
    }

    /**
     * 获取实体别名
     *
     * @param entityClazz
     * @return
     * @throws Exception
     */
    public static String getEntityAlias(Class<?> entityClazz) throws Exception {
        if (entityClazz == null) {
            return null;
        }

        EntityObject eo = EntityObjectLoader.getInstance().load(entityClazz);
        return eo.getAlias();
    }

    public static boolean isIdEmpty(Object model) throws Exception {
        if (model == null) {
            return true;
        }

        if (model instanceof Core) {
            return isEmpty(((Core) model).getId());
        }

        boolean flag = true;
        List<EntityField> fieldList = FieldUtils.getFieldList(model.getClass());
        for (EntityField ef : fieldList) {
            if (ef.getName().equals("id")) {
                Long id = (Long) ef.getValue(model);
                flag = isEmpty(id);
            }
        }

        return flag;
    }

    public static boolean isIdNotEmpty(Object model) throws Exception {
        return !isIdEmpty(model);
    }

    public static boolean isEmpty(Long id) {
        return id == null ? true : id > 0 ? false : true;
    }

    public static boolean isNotEmpty(Long id) {
        return !isEmpty(id);
    }

    public static void fill(Object target, Map<String, Object> map) {
        if (isSimpleType(target.getClass())) {
            return;
        }

        List<EntityField> fieldList = FieldUtils.getFieldList(target.getClass());
        map.forEach((key, val) -> {
            for (EntityField ef : fieldList) {
                try {
                    if (ef.getName().equals(key)) {
                        if (Collection.class.isAssignableFrom(ef.getJavaType())) {
                            if (Collection.class.isAssignableFrom(val.getClass())) {
                                Type[] argTypes = ef.getActualParameterTypeArgs();
                                if (argTypes != null && argTypes.length == 1 && argTypes[0] instanceof Class) {
                                    Collection sourceList = (Collection) val;
                                    Class<?> targetEntryClazz = (Class<?>) argTypes[0];
                                    List targetEntryList = new ArrayList();
                                    ef.setValue(target, targetEntryList);

                                    // 简单类型
                                    boolean isEntrySimpleType = isSimpleType(targetEntryClazz);
                                    for (Object sourceEntry : sourceList) {
                                        if (sourceEntry instanceof Map) {
                                            Object targetEntry = targetEntryClazz.newInstance();
                                            targetEntryList.add(targetEntry);
                                            fill(targetEntry, (Map) sourceEntry);
                                        } else {
                                            logger.error("设置属性{}，值{}失败！错误信息：{}", ef.getName(), val, "not supported!");
                                        }
                                    }
                                } else {
                                    logger.error("设置属性{}，值{}失败！错误信息：{}", ef.getName(), val, "not supported!");
                                }
                            } else {
                                logger.error("设置属性{}，值{}失败！错误信息：{}", ef.getName(), val, "not supported!");
                            }
                        } else {
                            TypeHandler handler = TypeHandlerHolder.getHandler(ef.getJavaType());
                            ef.setValue(target, handler.getValue(val));
                        }
                    }
                } catch (Exception e) {
                    logger.error("设置属性{}，值{}失败！错误信息：{}", ef.getName(), val, e.getMessage());
                }
            }
        });
    }

    /**
     * 转换model
     *
     * @param targetClazz
     * @param source
     * @param <T>
     * @return
     * @throws Exception
     */
    public static <T> T convert(Class<T> targetClazz, Object source) throws Exception {
        if (targetClazz == null || source == null) {
            return null;
        }

        T target = targetClazz.newInstance();
        copyProperties(source, target);
        return target;
    }

    /**
     * 复制model属性
     *
     * @param source
     * @param target
     * @throws Exception
     */
    public static void copyProperties(Object source, Object target) throws Exception {
        boolean isTargetVo = target.getClass().getSimpleName().endsWith("Vo");
        copyProperties(source, target, null, isTargetVo);
    }

    public static void copyProperties(Object source, Object target, PropertyProcessListener listener) throws Exception {
        boolean isTargetVo = target.getClass().getSimpleName().endsWith("Vo");
        copyProperties(source, target, listener, isTargetVo);
    }

    public static void copyProperties(Object source, Object target, boolean isTargetVo) throws Exception {
        copyProperties(source, target, null, isTargetVo);
    }

    /**
     * 复制model属性
     *
     * @param source
     * @param target
     * @throws Exception
     */
    public static void copyProperties(Object source, Object target, PropertyProcessListener listener, boolean isTargetVo) throws Exception {
        if (source == null || target == null) {
            return;
        }

        List<EntityField> sourceFieldList = FieldUtils.getFieldList(source.getClass());
        List<EntityField> targetFieldList = FieldUtils.getFieldList(target.getClass());

        for (EntityField sourceEf : sourceFieldList) {
            if (listener != null) {
                if (listener.processed(sourceEf.getName())) {
                    continue;
                }
            }

            //忽略static和transient字段
            if (isStaticOrTransient(sourceEf.getField())) {
                continue;
            }

            Object sourceValue = sourceEf.getValue(source);
            if (sourceValue == null) {
                continue;
            }

            // 扩展属性
            if (RootModel.EXTRA_VAR_NAME.equals(sourceEf.getName()) && sourceValue instanceof Map) {
                copyExtProperties(target, targetFieldList, (Map<String, Object>) sourceValue);
                continue;
            }

            // 复制
            copyProperty(targetFieldList, target, sourceEf, sourceValue, isTargetVo);
        }
    }

    private static void copyExtProperties(Object target, List<EntityField> targetFieldList, Map<String, Object> sourceExt) throws Exception {
        if (sourceExt == null || sourceExt.isEmpty()) {
            return;
        }

        EntityField targetEf = targetFieldList.stream()
                .filter(x -> RootModel.EXTRA_VAR_NAME.equals(x.getName()))
                .findFirst().orElse(null);
        if (targetEf != null && Map.class.isAssignableFrom(targetEf.getJavaType())) {
            Map<String, Object> targetExt = (Map<String, Object>) targetEf.getValue(target);
            if (targetExt == null) {
                targetExt = new HashMap<>();
                targetEf.setValue(RootModel.EXTRA_VAR_NAME, targetExt);
            }

            for (Map.Entry<String, Object> entry : sourceExt.entrySet()) {
                targetExt.put(entry.getKey(), entry.getValue());
            }
        }
    }

    private static void copyProperty(List<EntityField> targetFieldList, Object target, EntityField sourceEf, Object sourceValue, boolean isTargetVo) throws Exception {
        // 普通属性处理
        copyProperty(targetFieldList, target, sourceEf.getName(), sourceValue, isTargetVo);
    }

    private static void copyProperty(List<EntityField> targetFieldList, Object target, String sourceName, Object sourceValue, boolean isTargetVo) throws Exception {
        Class<?> sourceType = sourceValue.getClass();
        for (EntityField targetEf : targetFieldList) {
            //忽略static和transient字段
            if (isStaticOrTransient(targetEf.getField())) {
                continue;
            }

            // 不在这里处理extra属性
            if (RootModel.EXTRA_VAR_NAME.equals(targetEf.getName())) {
                continue;
            }

            // 同名属性
            if (sourceName.equals(targetEf.getName())) {
                // 集合类型
                if (Collection.class.isAssignableFrom(targetEf.getJavaType())) {
                    if (Collection.class.isAssignableFrom(sourceType)) {
                        Type[] argTypes = targetEf.getActualParameterTypeArgs();
                        if (argTypes != null && argTypes.length == 1 && argTypes[0] instanceof Class) {
                            Collection sourceList = (Collection) sourceValue;
                            Class<?> targetEntryClazz = (Class<?>) argTypes[0];
                            Collection targetEntryList = (Collection) targetEf.getValue(target);
                            // 简单类型
                            boolean isEntrySimpleType = isSimpleType(targetEntryClazz);
                            for (Object sourceEntry : sourceList) {
                                if (targetEntryList == null) {
                                    targetEntryList = new ArrayList<>();
                                    targetEf.setValue(target, targetEntryList);
                                }
                                if (isEntrySimpleType) {
                                    targetEntryList.add(sourceEntry);
                                } else {
                                    Object targetEntry = targetEntryClazz.newInstance();
                                    copyProperties(sourceEntry, targetEntry, isTargetVo);
                                    targetEntryList.add(targetEntry);
                                }
                            }
                        } else {
                            targetEf.setValue(target, sourceValue);
                        }
                    }
                } else if (Map.class.isAssignableFrom(targetEf.getJavaType())) {
                    if (Map.class.isAssignableFrom(sourceType)) {
                        Map sourceMap = (Map) sourceValue;
                        Map targetEntryMap = (Map) targetEf.getValue(target);
                        if (targetEntryMap == null) {
                            targetEntryMap = new HashMap();
                            targetEf.setValue(target, targetEntryMap);
                        }
                        for (Object item : sourceMap.entrySet()) {
                            if (item instanceof Map.Entry) {
                                Map.Entry entry = (Map.Entry) item;
                                targetEntryMap.put(entry.getKey(), entry.getValue());
                            }
                        }
                    }
                } else {
                    // 非集合类型
                    copyProperty(targetEf, target, sourceValue);
                }
                continue;
            }

            //  枚举类型 enum ==> enumDesc
            if ((targetEf.getName()).equals(sourceName + "Desc")) {
                if (ObjectEnum.class.isAssignableFrom(sourceType)) {
                    ObjectEnum actualValue = (ObjectEnum) sourceValue;
                    copyProperty(targetEf, target, actualValue.getDesc());
                    continue;
                }
            }

            // 枚举类型 enum ==> enumValue
            if ((targetEf.getName()).equals(sourceName + "Value")) {
                if (ObjectEnum.class.isAssignableFrom(sourceType)) {
                    ObjectEnum actualValue = (ObjectEnum) sourceValue;
                    copyProperty(targetEf, target, actualValue.getValue());
                    continue;
                }
            }

            // 关联对象类型 xxxId, xxxName ==> 对象 xxx.id, xxx.name
            if (sourceName.startsWith(targetEf.getName())
                    && isSimpleType(sourceType)
                    && !isSimpleType(targetEf.getJavaType())) {
                String name = StringUtils.firstAlpha2LowerCase(sourceName.substring(targetEf.getName().length()));

                List<EntityField> subFieldList = FieldUtils.getFieldList(targetEf.getJavaType());
                for (EntityField subEf : subFieldList) {
                    if (subEf.getName().equals(name)) {
                        Object subTarget = targetEf.getValue(target);
                        if (subTarget == null) {
                            subTarget = targetEf.getJavaType().newInstance();
                            targetEf.setValue(target, subTarget);
                        }
                        copyProperty(subEf, subTarget, sourceValue);
                        break;
                    }
                }
                continue;
            }

            // 关联对象类型 xxx.id, xxx.name ==> xxxId, xxxName
            if (targetEf.getName().startsWith(sourceName)
                    && isSimpleType(targetEf.getJavaType())
                    && !isSimpleType(sourceType)) {
                String name = StringUtils.firstAlpha2LowerCase(targetEf.getName().substring(sourceName.length()));

                List<EntityField> subFieldList = FieldUtils.getFieldList(sourceType);
                for (EntityField subEf : subFieldList) {
                    if (subEf.getName().equals(name)) {
                        Object subValue = subEf.getValue(sourceValue);
                        if (subValue == null) {
                            break;
                        }
                        copyProperty(targetEf, target, subValue);
                        break;
                    }
                }

                continue;
            }

            // 分录Dto/Vo
            if (List.class.isAssignableFrom(sourceType) &&
                    List.class.isAssignableFrom(targetEf.getJavaType())) {
                if (sourceName.equals(targetEf.getName() + "Dto")
                        || sourceName.equals(targetEf.getName() + "Vo")
                        || targetEf.getName().equals(sourceName + "Dto")
                        || targetEf.getName().equals(sourceName + "Vo")) {
                    Type[] argTypes = targetEf.getActualParameterTypeArgs();
                    if (argTypes != null && argTypes.length == 1) {
                        Class<?> targetEntryClazz = (Class<?>) argTypes[0];
                        List sourceList = (List) sourceValue;
                        for (Object sourceEntry : sourceList) {
                            Object targetEntry = targetEntryClazz.newInstance();
                            copyProperties(sourceEntry, targetEntry, isTargetVo);
                            List targetEntryList = (List) targetEf.getValue(target);
                            if (targetEntryList == null) {
                                targetEntryList = new ArrayList<>();
                                targetEf.setValue(target, targetEntryList);
                            }
                            targetEntryList.add(targetEntry);
                        }
                    }
                }
                continue;
            }
        }
    }

    private static void copyProperty(EntityField targetEf, Object target, Object value) throws Exception {
        if (targetEf.getJavaType().isAssignableFrom(value.getClass())) {
            if (List.class.isAssignableFrom(targetEf.getJavaType())) {
                Type[] argTypes = targetEf.getActualParameterTypeArgs();
                if (argTypes != null && argTypes.length == 1) {
                    TypeHandler handler = TypeHandlerHolder.getListHandler((Class<?>) argTypes[0]);
                    targetEf.setValue(target, handler.getValue(value));
                }
                return;
            }

            targetEf.setValue(target, value);
            return;
        }

        TypeHandler handler = null;
        //if (targetEf.getField().isAnnotationPresent(FieldDict.class) && DictItem.class.isAssignableFrom(targetEf.getJavaType())) {
        //    FieldDict fieldDict = targetEf.getField().getAnnotation(FieldDict.class);
        //    handler = TypeHandlerHolder.getDictHandler(fieldDict.value());
        //}

        if (handler == null) {
            handler = TypeHandlerHolder.getHandler(targetEf.getJavaType());
        }

        if (handler != null) {
            targetEf.setValue(target, handler.getValue(value));
        }
    }

    public static TypeHandler getTypeHandler(FieldObject fieldObject) throws Exception {
        if (fieldObject == null) {
            return null;
        }

        // Field field = fieldObject.getField();
        Class<?> javaType = fieldObject.getJavaType();

        TypeHandler typeHandler = null;
        if (fieldObject.isFieldTypeHandler()) {
            FieldTypeHandler fieldTypeHandler = fieldObject.getAnnotation(FieldTypeHandler.class);

            typeHandler = (TypeHandler) SpringUtil.getBean(fieldTypeHandler.value());
            if (typeHandler != null) {
                return typeHandler;
            }

            Constructor<?>[] constructors = Class.forName(fieldTypeHandler.value()).getConstructors();
            Constructor constructor1 = null;
            Constructor constructor2 = null;
            for (Constructor c : constructors) {
                Type[] parameterTypes = c.getGenericParameterTypes();
                if (parameterTypes.length == 1 && Class.class.isAssignableFrom(((ParameterizedType) parameterTypes[0]).getRawType().getClass())) {
                    constructor2 = c;
                    break;
                }

                if (parameterTypes.length == 0) {
                    constructor1 = c;
                }
            }

            if (constructor2 != null) {
                typeHandler = (TypeHandler<?>) constructor2.newInstance(javaType);
            } else {
                typeHandler = (TypeHandler<?>) constructor1.newInstance();
            }
        }

        //if (fieldObject.isDict() && fieldObject.isDictItem()) {
        //    typeHandler = TypeHandlerHolder.getDictHandler(fieldObject.getDictType());
        //}

        if (typeHandler == null) {
            typeHandler = TypeHandlerHolder.getHandler(javaType);
        }

        return typeHandler;
    }

    public static boolean isStaticOrTransient(Field field) {
        if (field == null) {
            return false;
        }

        if (Modifier.isStatic(field.getModifiers())
                || Modifier.isTransient(field.getModifiers())) {
            return true;
        }

        return false;
    }

    /**
     * 判断是否简单类型
     *
     * @param javaType
     * @return
     */
    public static boolean isSimpleType(Class<?> javaType) {
        if (simpleTypeHolder == null) {
            synchronized (SimpleTypeHolder.class) {
                Set<Class<?>> customSimpleTypes = new HashSet<>();
                customSimpleTypes.add(Timestamp.class);
                customSimpleTypes.add(BigDecimal.class);
                customSimpleTypes.add(StringEnum.class);
                customSimpleTypes.add(IntegerEnum.class);
                customSimpleTypes.add(BaseEnumEntry.class);
                simpleTypeHolder = new SimpleTypeHolder(customSimpleTypes, true);
            }
        }
        return simpleTypeHolder.isSimpleType(javaType);
    }

    public static boolean isNotSimpleType(Class<?> javaType) {
        return !isSimpleType(javaType);
    }

    /**
     * 将驼峰风格替换为下划线风格
     */
    public static String camelhumpToUnderline(String str) {
        final int size;
        final char[] chars;
        final StringBuilder sb = new StringBuilder(
                (size = (chars = str.toCharArray()).length) * 3 / 2 + 1);
        char c;
        for (int i = 0; i < size; i++) {
            c = chars[i];
            if (isUppercaseAlpha(c)) {
                sb.append('_').append(toLowerAscii(c));
            } else {
                sb.append(c);
            }
        }
        return sb.charAt(0) == '_' ? sb.substring(1) : sb.toString();
    }

    /**
     * 将下划线风格替换为驼峰风格
     */
    public static String underlineToCamelhump(String str) {
        Matcher matcher = camelPattern.matcher(str);
        StringBuilder builder = new StringBuilder(str);
        for (int i = 0; matcher.find(); i++) {
            builder.replace(matcher.start() - i, matcher.end() - i, matcher.group().substring(1).toUpperCase());
        }
        if (Character.isUpperCase(builder.charAt(0))) {
            builder.replace(0, 1, String.valueOf(Character.toLowerCase(builder.charAt(0))));
        }
        return builder.toString();
    }

    public static boolean isUppercaseAlpha(char c) {
        return (c >= 'A') && (c <= 'Z');
    }

    public static boolean isLowercaseAlpha(char c) {
        return (c >= 'a') && (c <= 'z');
    }

    public static char toUpperAscii(char c) {
        if (isLowercaseAlpha(c)) {
            c -= (char) 0x20;
        }
        return c;
    }

    public static char toLowerAscii(char c) {
        if (isUppercaseAlpha(c)) {
            c += (char) 0x20;
        }
        return c;
    }

    public static FieldObject getFieldObject(Class<?> clazz, String fieldName) throws Exception {
        if (clazz == null || fieldName == null) {
            return null;
        }

        EntityObject eo = EntityObjectLoader.getInstance().load(clazz);
        FieldObject fo = eo.getFieldObject(fieldName);
        if (fo == null) {
            logger.warn("{}.{} not exist.", clazz.getName(), fieldName);
            return null;
        }

        return fo;
    }

    public static Object getFieldValue(Class<?> clazz, String fieldName, Object model) throws Exception {
        FieldObject fo = getFieldObject(clazz, fieldName);

        if (fo == null || model == null) {
            return null;
        }

        return fo.getValue(model);
    }

    /**
     * 获取字段显示值,例如id显示name,枚举显示desc,数据字典显示对应的label等
     */
    public static Object getFieldShowValue(Class<?> clazz, String fieldName, Object model) throws Exception {
        return getShowValue(model, getFieldObject(clazz, fieldName));
    }

    /**
     * 获取字段显示值,例如id显示name,枚举显示desc,数据字典显示对应的label等
     */
    public static Object getShowValue(Object model, FieldObject fo) throws Exception {
        if (fo == null || model == null || fo.getValue(model) == null) {
            return null;
        }
        Object value = fo.getValue(model);
        if (fo.isEnum()) {
            // 枚举
            value = EnumUtils.getDesc(fo.getEnumType(), value);
        } else if (fo.isReference()) {
            // 关联字段
//            value = CacheUtils.getName(fo.getRefModelType(), StringUtils.toLong(value));
        }
        //else if (fo.isDict()) {
        //    // 数据字典
        //    value = DictUtils.getLabel(fo.getDictType(), value.toString());
        //}
        else if (fo.isBillType()) {
            // 单据类型
            String className = value.toString();
//            value = BillTypeUtils.getName(className);
//            if (value == null) {
//                // 兼容按编码获取
//                value = BillTypeUtils.getNameByNumber(className);
//            }
        }
        return value;
    }

    public static <T extends Core> void clearData(T model) throws Exception {
        if (model == null) {
            return;
        }
        model.setId(null);
//        if (model instanceof Generic) {
//            ((Generic) model).setCreateById(null);
//            ((Generic) model).setCreateTime(null);
//            ((Generic) model).setUpdateById(null);
//            ((Generic) model).setUpdateTime(null);
//        }
//        if (model instanceof SubBase) {
//            ((GenericEntry) model).setParent(null);
//            ((GenericEntry) model).setParentId(null);
//        }
//        if (model instanceof CoreBill) {
//            ((CoreBill) model).setNumber(null);
//            ((CoreBill) model).setStatus(null);
//            ((CoreBill) model).setAuditById(null);
//            ((CoreBill) model).setAuditTime(null);
//        }
//        if (model instanceof BaseData) {
//            ((BaseData) model).setName(null);
//            ((BaseData) model).setNumber(null);
//        }
        // model.setAuditTime(null);
        EntityObject entityObject = EntityObjectLoader.getInstance().load(model.getClass());
        List<FieldObject> fieldList = entityObject.getFieldList();
        for (FieldObject fo : fieldList) {
            if (fo.getJavaType() == null) {
                continue;
            }
            if ("parent".equals(fo.getName())) {
                continue;
            }
            if (List.class.isAssignableFrom(fo.getJavaType())) {
                // 分录
                List entryList = (List) fo.getValue(model);
                if (CollectionUtils.isEmpty(entryList)) {
                    continue;
                }
                for (Object entry : entryList) {
                    if (!(entry instanceof Core)) {
                        continue;
                    }
                    clearData((T) entry);
                }
            } else {
                // 子对象 递归
                if (!Core.class.isAssignableFrom(fo.getJavaType())) {
                    continue;
                }
                //
                Core subModel = (Core) fo.getValue(model);
                clearData(subModel);
            }
        }
    }

    public static void clearData(Object model, String... fields) throws Exception {
        if (model == null || fields.length == 0) {
            return;
        }
        if (model instanceof List) {
            List list = (List) model;
            for (Object subModel : list) {
                clearData(subModel, fields);
            }
            return;
        }
        EntityObject entityObject = EntityObjectLoader.getInstance().load(model.getClass());
        for (String field : fields) {
            FieldObject fieldObject = entityObject.getFieldObject(field);
            if (fieldObject != null) {
                fieldObject.setValue(model, null);
            }
        }
    }
}
